﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.IO;

namespace NuGetTemplatesPlus.Library {

    /// <summary>
    /// Inherit from this class to imlement a CodeGenerator based on System.CodeDom.
    /// You have to use the <see cref="InputFileContent"/> property to populate the <see cref="TargetNamespace"/> instance
    /// in the <see cref="GenerateCode"/> method.
    /// The class must have a public parameterless constructor.
    /// </summary>
    public abstract class CodeDomCodeGenerator : MarshalByRefObject, Interface.ICustomTool {

        /// <summary>
        /// Use the <see cref="InputFileContent"/> property to populate the <see cref="TargetNamespace"/> instance in this method        
        /// </summary>
        protected abstract void GenerateCode();

        /// <summary>
        /// Contains a reference to the initialized CodeNamespace that is the target of the generation.
        /// </summary>
        public CodeNamespace TargetNamespace { get; private set; }

        /// <summary>
        /// Allows access to the options used during generation.
        /// </summary>
        public CodeGeneratorOptions CodeGeneratorOptions { get; private set; }

        /// <summary>
        /// Returns the content of the input file.
        /// </summary>
        public string InputFileContent { get; private set; }

        #region ICustomTool Members

        byte[] Interface.ICustomTool.GenerateCode(Interface.ICustomToolSourceFile sourceFile, string parameter) {
            this.TargetNamespace = new CodeNamespace(sourceFile.FileNameSpace);

            var generatorFile = this.GetType().Assembly.Location;
            var versionInfo = System.Diagnostics.FileVersionInfo.GetVersionInfo(generatorFile);

            TargetNamespace.Comments.Add(new CodeCommentStatement("------------------------------------------------------------------------------"));
            TargetNamespace.Comments.Add(new CodeCommentStatement("<autogenerated>"));
            TargetNamespace.Comments.Add(new CodeCommentStatement(""));
            TargetNamespace.Comments.Add(new CodeCommentStatement("     This code was generated automaticly by a CodeGenerator"));
            TargetNamespace.Comments.Add(new CodeCommentStatement(""));
            TargetNamespace.Comments.Add(new CodeCommentStatement("     WARNING: Changes to this file may cause incorrect behavior and will be "));
            TargetNamespace.Comments.Add(new CodeCommentStatement("     lost if the code is regenerated!"));
            TargetNamespace.Comments.Add(new CodeCommentStatement(""));
            TargetNamespace.Comments.Add(new CodeCommentStatement("     Generator:                " + versionInfo.FileDescription));
            TargetNamespace.Comments.Add(new CodeCommentStatement("     Generator File Version:   " + versionInfo.FileVersion));
            TargetNamespace.Comments.Add(new CodeCommentStatement("     Runtime Version:          " + Environment.Version.ToString()));
            TargetNamespace.Comments.Add(new CodeCommentStatement(""));
            TargetNamespace.Comments.Add(new CodeCommentStatement("     Note: If you want to add additional members to the class, create a new "));
            TargetNamespace.Comments.Add(new CodeCommentStatement("     sourcefile and use the 'partial' keyword!"));
            TargetNamespace.Comments.Add(new CodeCommentStatement(""));
            TargetNamespace.Comments.Add(new CodeCommentStatement("</autogenerated>"));
            TargetNamespace.Comments.Add(new CodeCommentStatement("------------------------------------------------------------------------------"));

            this.CodeGeneratorOptions = new System.CodeDom.Compiler.CodeGeneratorOptions();
            this.InputFileContent = sourceFile.FileContent;
            GenerateCode();

            using (var stream = new MemoryStream()) {
                using (var writer = new StreamWriter(stream)) {
                    sourceFile.CodeDomProvider.GenerateCodeFromNamespace(this.TargetNamespace, writer, this.CodeGeneratorOptions);
                    writer.Flush();
                    return stream.ToArray();
                }
            }

        }

        #endregion
    }
}
